﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

using Collada141;

using RayDen.Library.Core.Collections;
using RayDen.Library.Core.Primitives;

using RayDen.Library.Entity.Scene;


namespace RayDen.Library.Data.Import.DAELoader {
    public class DataLoader : IGeometryLoader, IMaterialLoader {


        public SceneGeometryInfo Load(string fileName, bool invertNormals = false, params string[] additionalFiles)
        {
            return this.LoadData(fileName);
        }

        private Dictionary<Type, Action<object>> tagProcessors;
        private Dictionary<string, MaterialInfo> sceneMats;
        private Dictionary<string, ImageTextureInfo> textures;


        public DataLoader() {
            tagProcessors = new Dictionary<Type, Action<object>>();
            tagProcessors.Add(typeof(library_materials),this.ProcessMaterials);
        }

        private void ProcessMaterials(object mats) {
            var materials = mats as library_materials;
            if (materials == null)
                return;
        }

        public SceneGeometryInfo LoadData(string fileName) {
            sceneMats = new Dictionary<string, MaterialInfo>();
            textures = new Dictionary<string, ImageTextureInfo>();
            COLLADA model = COLLADA.Load(fileName);
            var result = new SceneGeometryInfo();
            var sceneVerts = new List<Point>();
            var meshes = new Dictionary<string, GeometryInfo>();
            var sceneNormals = new List<Vector>();
            var sceneTexData = new List<Vector>();
            var cams = new List<CameraInfo>();

            foreach (var item in model.Items) {
                if (item is library_materials) {
                    var materials = item as library_materials;

                    foreach (var material in materials.material) {

                    }

                }


                if (item is library_cameras) {
                    var cameras = item as library_cameras;
                    foreach (var camera in cameras.camera) {
                        if (camera.optics.technique.Length > 0) {
                            var t = camera.optics.technique[0];

                        }
                        var camItem = new CameraInfo() { CameraName = camera.id.Replace("-lib", string.Empty) };
                        cams.Add(camItem);
                    }
                }

                var geometries = item as library_geometries;
                if (geometries == null)
                    continue;

                // Iterate on geomerty in library_geometries 
                foreach (var geom in geometries.geometry) {
                    var meshInfo = geom.Item as mesh;
                    if (meshInfo == null)
                        continue;

                    var mesh = new GeometryInfo() { Name = geom.name.Replace("Mesh", string.Empty) };
                    meshes.Add(mesh.Name, mesh);


                    // Dump source[] for geom
                    foreach (var source in meshInfo.source) {
                        var float_array = source.Item as float_array;
                        if (float_array == null)
                            continue;

                        if (source.id.Contains("Position")) {
                            sceneVerts.AddRange(float_array.Values.ToPointList());
                            mesh.VertexData = new FloatStore(float_array.Values.ToFloatArray());
                        }
                        else if (source.id.Contains("Normal")) {
                            sceneNormals.AddRange(float_array.Values.ToVectorList());
                            mesh.NormalData = new FloatStore(float_array.Values.ToFloatArray());
                        }
                        else if (source.id.Contains("UV")) {
                            sceneTexData.AddRange(float_array.Values.ToVectorList());
                            mesh.TextureData = new FloatStore(float_array.Values.ToFloatArray());
                        }
                        else {
                            Console.WriteLine("Geometry {0} Unknown source {1} : ", geom.id, source.id);
                        }

                        Console.Write("Geometry {0} source {1} : ", geom.id, source.id);

                        /*
                        foreach (var mesh_source_value in float_array.Values)
                            Console.Write("{0} ", mesh_source_value);
                         */
                        Console.WriteLine();
                    }

                    // Dump Items[] for geom
                    foreach (var meshItem in meshInfo.Items) {

                        if (meshItem is vertices) {
                            var vertices = meshItem as vertices;
                            var inputs = vertices.input;
                            foreach (var input in inputs)
                                Console.WriteLine("\t Semantic {0} Source {1}", input.semantic, input.source);
                        }
                        else if (meshItem is triangles) {
                            var triangles = meshItem as triangles;
                            var inputs = triangles.input;
                            foreach (var input in inputs)
                                Console.WriteLine("\t Semantic {0} Source {1} Offset {2}", input.semantic, input.source,
                                                  input.offset);
                            Console.WriteLine("\t Indices {0}", triangles.p);
                        }
                        else if (meshItem is polygons) {
                            var polygons = meshItem as polygons;
                            var inputs = polygons.input;
                            foreach (var input in inputs)
                                Console.WriteLine("\t Semantic {0} Source {1} Offset {2}", input.semantic, input.source,
                                                  input.offset);

                            var indexes = polygons.Items.SelectMany(u => u.ToString().Split(' ')).Select(ix => int.Parse(ix, CultureInfo.InvariantCulture)).ToArray();
                            mesh.IndexData = new IntStore(indexes.Where((x, i) => i % 3 == 0));
                            mesh.NormalIndexData = new IntStore(indexes.Where((x, i) => (i + 1) % 3 == 0));
                            mesh.TextureIndexData = new IntStore(indexes.Where((x, i) => (i + 2) % 3 == 0));

                            Console.WriteLine("\t Indices {0}", indexes.ToArray());
                        }
                    }
                }
            }


            foreach (var node in model.Items.OfType<library_visual_scenes>().SelectMany(sceneNode => sceneNode.visual_scene.SelectMany(it => it.node))) {
                if (node.instance_camera != null && node.instance_camera.Length > 0) {
                    var cam = cams.FirstOrDefault(m => m.CameraName.Equals(node.id));
                    if (cam != null) {
                        cam.PosTransform = new Matrix4x4(((matrix)node.Items[0]).Values);
                        cam.Position = new Point(cam.PosTransform[3], cam.PosTransform[7], cam.PosTransform[11]);
                        cam.Up = new Vector(0, 0, -1f);
                        Console.WriteLine("Camera {0} initialized", cam.CameraName);
                    }
                    continue;
                }

                if (cams.Any(cam => node.id.Contains(cam.CameraName))) {
                    var cam = cams.FirstOrDefault(cm => node.id.Contains(cm.CameraName));
                    if (cam != null) {
                        cam.DirTransform = new Matrix4x4(((matrix)node.Items[0]).Values);
                        cam.Direction = new Vector(cam.DirTransform[3], cam.DirTransform[7], cam.DirTransform[11]);
                        Console.WriteLine("Camera {0} initialized", cam.CameraName);
                    }
                    continue;
                }

                if (node.instance_geometry != null && node.instance_geometry.Length > 0) {
                    var mesh = meshes[node.id];
                    if (mesh != null) {
                        var instanceMaterials = node.instance_geometry[0].bind_material;
                        if (instanceMaterials != null)
                            mesh.MaterialName = instanceMaterials.technique_common[0].symbol;
                        mesh.Transform = new Matrix4x4(((matrix)node.Items[0]).Values);
                        Console.WriteLine(mesh);
                    }
                }
            }



            result.Geometry = meshes.Values.ToArray();
            result.Vertices = sceneVerts.ToArray();
            result.Normals = sceneNormals.ToArray();
            result.TexCoords = sceneTexData.ToArray();
            result.Cameras = cams.ToArray();

            return result;
        }

        #region IMaterialLoader Members

        public MaterialInfo[] LoadMaterials(string fileName, params string[] paths) {
            return sceneMats.Values.ToArray();
        }

        #endregion
    }
}

